#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using KLib.Input;
#endregion

namespace KLib.Screens
{
    /// <summary>
    /// The ScreenManager component is used to hold, update, draw, and manage what GameScreen is drawn at a time.
    /// To use the Manager, simply load it to the game, and add one or more GameScreens. GameScreens should be created by inheriting
    /// the abstract GameScreen Class, and then adding Game logic to the new class.
    /// </summary>
    public class ScreenManager : KLib.DrawableGameComponent
    {

        #region Fields
        private List<GameScreen> screens;
        private List<GameScreen> screensToUpdate;
        private List<GameScreen> screensToDraw;
        private Stack<GameScreen> screenHistory;

        private SpriteBatch spriteBatch;
        private IInput input;
        private RenderTarget2D buffer;

        private Rectangle screenSize;
        GameScreen currentScreen;
        #endregion

        #region Properties
        /// <summary>
        /// Gets a List of all the GameScreens being managed by the ScreenManager
        /// </summary>
        public List<GameScreen> Screens
        {
            get { return screens; }
        }

        /// <summary>
        /// Gets the screen history of the screen manager
        /// </summary>
        public Stack<GameScreen> History
        {
            get { return screenHistory; }
        }

        /// <summary>
        /// Gets a SpriteBatch from the screen manager for use on child screens
        /// </summary>
        public SpriteBatch SpriteBatch
        {
            get { return spriteBatch; }
        }

        /// <summary>
        /// Gets Input that is given to the ScreenManager
        /// </summary>
        [OptionalService]
        public IInput Input
        {
            get { return input; }
            private set { input = value; }
        }

        /// <summary>
        /// Gets the ContentManger of the ScreenManager for use in child screens
        /// </summary>
        public Microsoft.Xna.Framework.Content.ContentManager Content
        {
            get { return Game.Content; }
        }

        /// <summary>
        /// Gets the Max size of a screen (The Size of the game window)
        /// </summary>
        public Rectangle ScreenSize
        {
            get { return screenSize; }
        }
        #endregion

        #region Initialization
        public ScreenManager(Game game)
            : base(game)
        {

        }

        public override void Initialize()
        {
            this.screenSize = new Rectangle(0, 0, Game.GraphicsDevice.PresentationParameters.BackBufferWidth, Game.GraphicsDevice.PresentationParameters.BackBufferHeight);
            this.screens = new List<GameScreen>();
            this.screensToUpdate = new List<GameScreen>();
            this.screensToDraw = new List<GameScreen>();
            this.screenHistory = new Stack<GameScreen>();
            base.Initialize();
        }

        protected override void LoadContent()
        {
            base.LoadContent();
            spriteBatch = new SpriteBatch(GraphicsDevice);
        }
        #endregion

        #region Methods
        /// <summary>
        /// Updates all the GameScreens that are requesting an Update
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            for (int i = 0; i < screens.Count; i++)
            {
                if (screens[i].RequiresUpdate)
                    screens[i].Update(gameTime);
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// Draws all the GameScreens that are requesting to be Drawn
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            for (int i = 0; i < screens.Count; i++)
			{
                if (screens[i].RequiresDraw)
                    screens[i].Draw(gameTime);
			}
            base.Draw(gameTime);
        }

        /// <summary>
        /// Adds a new screen to the ScreenManager. The Screen being added is also Initialized, get it's Content Loaded and 
        /// </summary>
        /// <param name="screen"></param>
        public void AddScreen(GameScreen screen)
        {
            if (screens.Contains(screen))
            {
                screen.MakeActive();
                return;
            }
            if (currentScreen != null)
                screenHistory.Push(currentScreen);

            screen.ScreenManager = this;
            screen.Initialize();
            screen.LoadContent();
            screens.Add(screen);
            currentScreen = screen;
            screen.MakeActive();
        }

        /// <summary>
        /// Removes a screen perminantly from the ScreenManager, and disposes of it
        /// *****NOTE*****It actually DOES NOT perminantly remove it at this time
        /// </summary>
        /// <param name="screen"></param>
        /// <param name="doTransition"></param>
        public void RemoveScreen(GameScreen screen, bool doTransition)
        {
            screen.MakeInactive(doTransition);
        }

        /// <summary>
        /// Makes the specified screent the active screen
        /// </summary>
        /// <param name="screen"></param>
        public void MakeScreenActive(GameScreen screen)
        {
            //Only one screen can be active at a time, so make all others inactive
            for (int i = 0; i < screens.Count; i++)
            {
                screens[i].MakeInactive(false);
            }
            screen.MakeActive();
        }

        /// <summary>
        /// Makes a screen of Type T the active screen.
        /// </summary>
        /// <typeparam name="T">The Type of GameScreen that should be made active</typeparam>
        /// <returns>Returns True if a match is found, returns false if no match is found</returns>
        public bool MakeScreenActive<T>()
        {
            //Only one screen can be active at a time, so make all others inactive
            for (int i = 0; i < screens.Count; i++)
            {
                screens[i].MakeInactive(false);
            }
            //Search through all the screens untill one is found that matches.
            for (int i = 0; i < screens.Count; i++)
            {
                if (screens[i].GetType() == typeof(T))
                {
                    screens[i].MakeActive();
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// If Possible, brings the focus back to the last screen that was active before the current screen.
        /// </summary>
        public void Back()
        {
            if (screenHistory.Count <= 0
                || screenHistory.Peek() == null)
                return;

            currentScreen.MakeInactive(true);
            currentScreen = screenHistory.Pop();
            MakeScreenActive(currentScreen);
            if (screenHistory.Count <= 0)
            {
                screenHistory.Push(currentScreen);
            }
        }
        #endregion
    }
}